import asyncio
import json
import websockets
from typing import Dict, List, Callable
from HttpMgr import HttpMgr
import aiomysql

class WebsocketClient:
    _instance = None
    RECONNECT_INTERVAL = 1800  # 30分钟重连
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialize()
        return cls._instance
    
    def _initialize(self):
        self.websocket = None
        self.is_connected = False
        self.reconnect_task = None
        self.event_connect_ok = None
        self.local_epc_lists = {
            0: [], 5: [], 10: [], 15: [], 20: [],
            25: [], 30: [], 35: [], 40: [], 42: []
        }
        self.player_cache = {}  # 选手缓存
        self.config = self.load_config()
        HttpMgr().add_callback(self.on_config_update)
        
    def load_config(self) -> Dict:
        return {
            "wssurl": "ws://localhost:8765",
            "device_mapping": HttpMgr._instance.device_cache if HttpMgr._instance else {}
        }

    async def on_config_update(self, new_config):
        print("设备配置更新触发")
        self.config["device_mapping"] = new_config
        print(self.config["device_mapping"])
        # if self.is_connected:
        #     await self.reconnect()
    
    async def connect(self):
        print("嗯？")
        try:
            if HttpMgr._instance:
                print("咋回事")
                await HttpMgr._instance.RefreshDeviceInfo()
                print("http init ok")
            else:
                print("HttpMgr实例未初始化")
                return
            self.websocket = await websockets.connect(self.config["wssurl"])
            self.is_connected = True
            await self.on_open()
            asyncio.create_task(self.listen_messages())
        except Exception as e:
            print(f"连接失败: {str(e)}")
            await self.schedule_reconnect()
    
    async def on_open(self):
        print("连接成功")
        await self.send("haha")
        if self.event_connect_ok:
            self.event_connect_ok()
    
    async def listen_messages(self):
        try:
            async for message in self.websocket:
                await self.on_message(message)
        except websockets.ConnectionClosed:
            await self.on_close()
    
    async def on_message(self, message: str):
        print(f"收到消息: {message}")
        
        # 处理特殊文本消息
        if message in ('pong', 'haha'):
            print(f"收到系统消息: {message}")
            return

        try:
            data = json.loads(message)
        except json.JSONDecodeError as e:
            print(f"无效的JSON格式消息: {message}\n错误详情: {str(e)}")
            return
        
        if not isinstance(data, dict):
            print(f"非字典类型的消息: {message}")
            return
        
        if data.get("type") == "chipData":
            await self.process_chip_data(data)
        elif data.get("type") == "ping":
            await self.send("pong")
        elif data.get("type") == "init":
            await self.handle_init(data)
    
    async def process_chip_data(self, data: Dict):
        mac = data["mac"]
        km_type = self.get_km_type_by_mac(mac)
        
        if km_type is not None:
            new_epcs = [
                (item["epc"], int(item["times"])) for item in data["data"]
                if item["epc"] not in self.local_epc_lists[km_type]
            ]
            self.local_epc_lists[km_type].extend(new_epcs)
            # 这里调用全局参数更新逻辑
            print(f"更新公里数 {km_type} 通过人数: {len(self.local_epc_lists[km_type])}")
            # 存入数据库
            from Main import pool
            async with pool.acquire() as conn:
                async with conn.cursor(aiomysql.DictCursor) as cur:
                    # 批量插入每个epc记录
                    # 批量查询未缓存选手
                    uncached_epcs = [epc for epc, _ in new_epcs if epc not in self.player_cache]
                    if uncached_epcs:
                        await cur.execute("""
                            SELECT id, name, gender FROM curPlayerList 
                            WHERE id IN %s
                        """, (tuple(uncached_epcs),))
                        found_rows = await cur.fetchall()
                        found_epcs = set()
                        for row in found_rows:
                            # 每个人过每个点的成绩记录
                            self.player_cache[row['id']] = {
                                'name': row['name'],
                                'gender': row['gender'],
                                0: 0, 5: 0, 10: 0, 15: 0, 20: 0, 
                                25: 0, 30: 0, 35: 0, 40: 0, 42: 0
                            } 
                            found_epcs.add(row['id'])
                        # 处理未找到的EPC
                        missing_epcs = [epc for epc in uncached_epcs if epc not in found_epcs]
                        for epc in missing_epcs:
                            self.player_cache[epc] = {
                                'name': '-',
                                'gender': '-',
                                0: 0, 5: 0, 10: 0, 15: 0, 20: 0,
                                25: 0, 30: 0, 35: 0, 40: 0, 42: 0
                            }
                    # 处理每个EPC的成绩并构建参数
                    params = []  #不处理部分
                    params2 = [] #处理部分
                    for epc, times in new_epcs:
                        # 更新当前EPC的成绩
                        self.player_cache[epc][km_type] = int(times)
                        # 数据经过处理，即自己计算出分段成绩部分
                        if km_type == 0:
                            params2.append(
                                (km_type, epc, '00:00:00', self.player_cache[epc]['name'], self.player_cache[epc]['gender'])
                            )
                        else:
                            if self.player_cache[epc][0] == 0:
                                params2.append(
                                    (km_type, epc, '00:00:00', self.player_cache[epc]['name'], self.player_cache[epc]['gender'])
                                )
                            else:
                                # 计算时间差
                                time_diff = times - int(self.player_cache[epc][0])
                                time_diff = time_diff / 1000
                                print(f"新时间{times} - 老时间{int(self.player_cache[epc][0])} = {time_diff}")
                                # 转换为时分秒格式
                                hours, remainder = divmod(int(time_diff), 3600)
                                minutes, seconds = divmod(remainder, 60)
                                time_str = f"{hours:02}:{minutes:02}:{seconds:02}"
                                params2.append(
                                    (km_type, epc, time_str, self.player_cache[epc]['name'], self.player_cache[epc]['gender'])
                                )
                        # 数据不处理部分 构建每条源数据存储 插入 参数
                        # 构建参数
                        params.append(
                            (km_type, epc, times, self.player_cache[epc]['name'], self.player_cache[epc]['gender'])
                        )
                    # 批量插入每个epc记录
                    # 不处理部分的数据库存储
                    # try:
                    #     await cur.executemany("""
                    #         INSERT INTO curPassDatas (type, id, time, name, timestamp)
                    #         VALUES (%s, %s, %s, %s, NOW())
                    #     """, params)
                    # except aiomysql.IntegrityError as e:
                    #     print(f"[唯一性校验] 重复数据已跳过: type={params[0][0]} id={params[0][1]}")
                    for param in params:
                        try:
                            await cur.execute("""
                                INSERT INTO curPassDatas (type, id, time, name, timestamp, gender)
                                VALUES (%s, %s, %s, %s, %s, NOW())
                            """, param)
                        except aiomysql.IntegrityError as e:
                            print(f"[唯一性校验] 跳过重复数据: type={param[0]} id={param[1]}")
                    await conn.commit()
                    # 处理部分的数据库存储
                    for param in params2:
                        try:
                            await cur.execute("""
                                INSERT INTO curPassResult (type, id, time, name, timestamp, gender)
                                VALUES (%s, %s, %s, %s, %s, NOW())
                            """, param)
                        except aiomysql.IntegrityError as e:
                            print(f"[唯一性校验] 重复数据已跳过: type={params[0]} id={params[1]}")
                    await conn.commit()
                    
    
    def get_km_type_by_mac(self, mac: str) -> int:
        # 遍历device_mapping字典
        for km_type, mac_list in self.config["device_mapping"].items():
            if mac in mac_list:
                if km_type == "total_score":
                    return 42
                if km_type == "score40k":
                    return 40
                if km_type == "score35k":
                    return 35
                if km_type == "score30k":
                    return 30
                if km_type == "score25k":
                    return 25
                if km_type == "score20k":
                    return 20
                if km_type == "score15k":
                    return 15
                if km_type == "score10k":
                    return 10
                if km_type == "score5k":
                    return 5
                if km_type == "start_time":
                    return 0
                return None
        return None

    async def send(self, message: str):
        if self.websocket:
            await self.websocket.send(message)
    
    async def on_close(self):
        print("连接关闭")
        self.is_connected = False
        await self.schedule_reconnect()
    
    async def schedule_reconnect(self):
        if self.reconnect_task is None:
            self.reconnect_task = asyncio.create_task(self.reconnect())
    
    async def close(self):
        print("主动关闭连接")
        if self.websocket:
            await self.websocket.close()
            self.is_connected = False
            print("主动关闭连接")

    async def reconnect(self):
        if self.is_connected:
            await self.close()
        print("立即尝试重连...")
        await self.connect()
        self.reconnect_task = None
    
    async def close(self):
        if self.websocket:
            await self.websocket.close()
            self.is_connected = False

# # 使用示例
# async def main():
#     client = WebsocketClient()
#     client.event_connect_ok = lambda: print("连接成功回调!")
#     await client.connect()
    
#     # 保持连接
#     while True:
#         await asyncio.sleep(1)

# if __name__ == "__main__":
#     asyncio.run(main())